package com.spzx.payment.service.impl;

import java.math.BigDecimal;
import java.util.*;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.DateUtils;
import com.spzx.common.rabbitmq.service.RabbitmqService;
import com.spzx.order.api.RemoteOrderService;
import com.spzx.order.domain.OrderInfo;
import com.spzx.payment.config.AlipayConfig;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.spzx.payment.mapper.PaymentInfoMapper;
import com.spzx.payment.domain.PaymentInfo;
import com.spzx.payment.service.IPaymentInfoService;

/**
 * 付款信息Service业务层处理
 *
 * @author atguigu
 * @date 2024-12-28
 */
@Service
@Slf4j
public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, PaymentInfo> implements IPaymentInfoService
{
    @Resource
    private PaymentInfoMapper paymentInfoMapper;
    @Resource
    RemoteOrderService remoteOrderService;
    @Resource
    RabbitmqService rabbitmqService;

    /**
     * 查询付款信息列表
     *
     * @param paymentInfo 付款信息
     * @return 付款信息
     */
    @Override
    public List<PaymentInfo> selectPaymentInfoList(PaymentInfo paymentInfo)
    {
        return paymentInfoMapper.selectPaymentInfoList(paymentInfo);
    }
    //异步执行
    @Async  //必须启用异步任务！  使用EnableAsync注解
    @Override
    public void saveAsync(OrderInfo orderInfo) {
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setUserId(orderInfo.getUserId());
        paymentInfo.setOrderNo(orderInfo.getOrderNo());
        paymentInfo.setPayType(2L);
//            paymentInfo.setTradeNo(); //支付宝 或者 微信支付的交易流水号
        paymentInfo.setAmount(orderInfo.getTotalAmount()); //如果涉及优惠券 、 运费 需要计算！
        paymentInfo.setContent("尚品甄选商品");
        paymentInfo.setPaymentStatus("0");
        this.save(paymentInfo);
    }

    @Override
    public String submitAlipay(String orderNo) {

        // 商户订单号，商户网站订单系统中唯一订单号，必填
        String out_trade_no = orderNo;

        R<OrderInfo> orderInfoR = remoteOrderService.getOrderByOrderNo(orderNo);
        if(orderInfoR.getCode()!=200){
            throw new ServiceException("订单不存在!!");
        }
        OrderInfo orderInfo = orderInfoR.getData();
        // 订单名称，必填
        String subject = "尚品甄选商品";
        // 付款金额，必填:  可以根据订单编号查询得到
        String total_amount = orderInfo.getTotalAmount().toString();

        // 销售产品码 必填   不能修改！！！！！！！！！！！！！
        String product_code = "QUICK_WAP_WAY";
        /**********************/
        // SDK 公共请求类，包含公共请求参数，以及封装了签名与验签，开发者无需关注签名与验签
        //调用RSA签名方式
        //AlipayClient: 需要引入alipay-sdk的依赖
        AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
                AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT,
                AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,
                AlipayConfig.SIGNTYPE);
        AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();

        // 封装请求支付信息
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setOutTradeNo(out_trade_no);
        model.setSubject(subject);
        model.setTotalAmount(total_amount);
        model.setProductCode(product_code);
        alipay_request.setBizModel(model);
        // 设置异步通知地址
        alipay_request.setNotifyUrl(AlipayConfig.notify_url);
        // 设置同步地址
        alipay_request.setReturnUrl(AlipayConfig.return_url);

        // form表单生产
        String form = "";
        try {
            // 调用SDK生成自动提交的表单：  表单字符串中包含了js脚本 触发表单提交
            form = client.pageExecute(alipay_request).getBody();
            System.out.println(form);
            /*
                SpringContext包中提供了很多工具简化开发：
                    1、springCache: 自动缓存管理
                        EnableCacheing + Cacheable 注解 可以实现自动缓存管理
                    2、springAsync : 异步任务
                        EnableAsync + Async注解标注的方法会自动异步执行
             */
            //异步保存交易日志
            saveAsync(orderInfo);

            return form; //
        } catch (AlipayApiException e) {
            // TODO Auto-generated catch block
//            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    public String notifyUrl(Map<String, String> params) {
        //获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
        //商户订单号
        String out_trade_no = params.get("out_trade_no");
        //支付宝交易号
        String trade_no =  params.get("trade_no");
        //交易状态
        String trade_status = params.get("trade_status");
        try {
            //验签： 使用支付宝的公钥 对支付宝私钥加签的结果进行验证
            boolean verify_result = AlipaySignature
                    .rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY,
                            AlipayConfig.CHARSET,
                            AlipayConfig.SIGNTYPE);

            if(verify_result){//验证成功  表示参数没有被修改
                //////////////////////////////////////////////////////////////////////////////////////////
                //请在这里加上商户的业务逻辑程序代码
                //1、保证幂等性：因为支付宝异步回调可能会调用多次 同一个订单的异步回调只需要处理一次
                //  用户提交支付宝支付时 可以保存交易日志
                //  异步回调通知中  可以根据订单号获取 交易日志 判断日志的状态 如果已经更新过 不在处理
                PaymentInfo paymentInfo = getOne(Wrappers.lambdaQuery(PaymentInfo.class)
                                .eq(PaymentInfo::getOrderNo, out_trade_no)
                        //.eq(PaymentInfo::getPaymentStatus, "0")
                );//根据商家订单号 查询 未支付的交易日志
                if(paymentInfo==null){
                    log.error("非法回调");
                    return "fail";
                }
                if(!paymentInfo.getPaymentStatus().equals("0")){
                    log.info("重复回调");
                    return "success";
                }
                String buyerPayAmount = params.get("buyer_pay_amount");
                //2、验证用户支付金额
                if(paymentInfo.getAmount().compareTo(new BigDecimal(buyerPayAmount))!=0){
                    log.error("用户支付金额错误，应付金额：{} , 实付金额:{} ,回调参数:{}" ,paymentInfo.getAmount(),
                            buyerPayAmount , JSON.toJSONString(params));
                    return "fail";
                }
                //   用户在支付宝支付的金额  和 我们本地服务器存储的订单应付金额 进行比较
                //3、判断支付宝用户支付的交易状态是不是交易完成
                if(!"TRADE_SUCCESS".equals(trade_status)){
                    //交易不是完成状态
                    log.error("支付交易状态异常:{}" , JSON.toJSONString(params));
                    return "fail";
                }
                //4、主业务：更新交易日志的状态、修改订单的支付状态、扣减购买商品的库存和加销量、更新订单日志状态...
                //——请根据您的业务逻辑来编写程序（以下代码仅作参考）——
                //4.1 更新交易日志的状态
                paymentInfo.setPaymentStatus("1");
                paymentInfo.setCallbackTime(new Date());
                paymentInfo.setCallbackContent(JSON.toJSONString(params));
                paymentInfo.setTradeNo(trade_no);
                updateById(paymentInfo);
                //4.2 发送mq消息：修改订单的支付状态、扣减购买商品的库存和加销量、更新订单日志状态...
                Map<String,String> map = new HashMap<>();
                map.put("orderNo" , out_trade_no);
                map.put("type" , "1"); // 1表示支付成功  ，0 表示取消支付..

                rabbitmqService.sendMsg("spzx.payment.exchange",
                        "spzx.pay.success.msg",
                        map);
                //支付宝会重复调用当前接口，直到接收到我们给的success字符串响应
                return "success";
                //////////////////////////////////////////////////////////////////////////////////////////
            }else{//验签失败：
                return "fail";
            }
        } catch (Exception e) {
            log.error("支付宝支付异步回调处理失败: {}" , JSON.toJSONString(params));
            return "fail";
        }
    }

}
